﻿using System;
using System.Collections.Generic;
using System.Linq;
using Granite.Modeling;
using Granite.DataAnnotations;
using Granite.Modeling.Internals;
using System.Collections.ObjectModel;
using Granite.Metadata;

namespace Rifts.Models.Characters
{
	public interface IAttributeClass
	{
		void BumpAttribute(string attributeName, int value);

		void SetAttribute(string attributeName, int p);
	}

	public partial class Character : EditableObjectModelBase, IAttributeClass
	{
		public Character()
		{
			Skills.CollectionChanged += (s, e) => PushIQBonus();
			CombatAbilities.CollectionChanged += (s, e) => RecalculateStyles();
			UnarmedAttacks.CollectionChanged += (s, e) => RecalculateStyles();
			FightingStyles.ItemAdded += (s, e) => e.Item.UpdateCalculatedValues(this);
			Proficiencies.CollectionChanged += (s, e) => RecalculateStyles();
		}

		#region core attributes






		#endregion

		#region psionics

		public int? IspBase
		{
			get { return Get<int?>("IspBase"); }
			set { Set(value, "IspBase"); }
		}

		[CalculatedField("IspBase,ME,IspFromMe")]
		public int? Isp
		{
			get { return IspBase + (ME * IspFromME); }
		}

		/// <summary>
		/// This is a multiplier
		/// </summary>
		public int IspFromME
		{
			get { return Get<int>(0, "IspFromME"); }
			set { Set(value, "IspFromMe"); }
		}

		#endregion

		#region abilities

		#endregion

		#region magic


		public int Ppe
		{
			get { return Get<int>("Ppe"); }
			set { Set(value, "Ppe"); }
		}

		public int SpellStrength
		{
			get { return Get<int>("SpellStrength"); }
			set { Set(value, "SpellStrength"); }
		}


		#endregion

		#region combat


		public int? SDC
		{
			get { return Get<int?>("SDC"); }
			set { Set(value, "SDC"); }
		}

		public int? MDC
		{
			get { return Get<int?>("MDC"); }
			set { Set(value, "MDC"); }
		}

		public int? HPBase
		{
			get { return Get<int?>("HPBase"); }
			set { Set(value, "HPBase"); }
		}

		[CalculatedField("HPBase,PE")]
		public int? HP
		{
			get { return HPBase + PE; }
		}












		#endregion



		#region skills


		public int MaxOccSkillsFromIQ(int occSkills, Random random)
		{
			if (IQ >= 7)
				return occSkills;
			if (IQ >= 5)
				return occSkills - 4;
			if (IQ >= 3)
				return (int)Math.Ceiling((decimal)occSkills / 2M);
			return random.D4() + 1;

		}

		public int OccRelatedSkillsFromIQ(int occSkills, Random random)
		{
			if (IQ >= 9)
				return occSkills;
			if (IQ >= 5)
				return (int)Math.Ceiling((decimal)occSkills / 2M);
			if (IQ >= 3)
				return random.D4();
			return 0;
		}

		public int SecondarySkillsFromIQ(int secondarySkills, Random random)
		{
			if (IQ >= 9)
				return secondarySkills;
			if (IQ >= 7)
				return secondarySkills + random.D4();
			if (IQ > 5)
				return secondarySkills + random.D4() + 2;

			return random.D4();
		}

		public SkillCollection Skills
		{
			get { return GetNew<SkillCollection>("Skills"); }
		}

		#endregion

		public AbilityCollection Abilities
		{
			get { return GetNew<AbilityCollection>("Abilities"); }
		}




		#region Saving throws



		public int SaveBase
		{
			get { return Get<int>("SaveBase"); }
			set { Set(value, "SaveBase"); }
		}

		public int SaveHorror
		{
			get { return Get<int>("SaveHorror"); }
			set { Set(value, "SaveHorror"); }
		}

		public int SavePsionic
		{
			get { return Get<int>("SavePsionic"); }
			set { Set(value, "SavePsionic"); }
		}

		public int SaveMindControl
		{
			get { return Get<int>("SaveMindControl"); }
			set { Set(value, "SaveMindControl"); }
		}

		public int SaveMagic
		{
			get { return Get<int>("SaveMagic"); }
			set { Set(value, "SaveMagic"); }
		}

		public int SavePossession
		{
			get { return Get<int>("SavePossession"); }
			set { Set(value, "SavePossession"); }
		}

		public int SaveIllusions
		{
			get { return Get<int>("SaveIllusions"); }
			set { Set(value, "SaveIllusions"); }
		}
		#endregion



		public void BumpAttribute(string attributeName, int value)
		{
			var propertyName = MapAttribute(attributeName);

			var oldValue = GetAttribute(propertyName);
			var newValue = (oldValue ?? 0) + value;
			Properties.Set(newValue, PropertySetModes.FixCasing | PropertySetModes.RaiseChangedEvent | PropertySetModes.ValidateProperty | PropertySetModes.ValidateObject, propertyName);
		}

		public void BumpSkill(string skillName, int bonus)
		{
			foreach (var skill in Skills.FindAllByName(skillName))
			{
				skill.CrossSkillBonus = skill.CrossSkillBonus.AddNull(bonus);
			}
		}

		public void SetAttribute(string attributeName, int value)
		{

			var propertyName = MapAttribute(attributeName);

			if (Properties.Metadata.Properties.Contains(propertyName))
				Properties.Set(value, PropertySetModes.FixCasing | PropertySetModes.RaiseChangedEvent | PropertySetModes.ValidateProperty | PropertySetModes.ValidateObject, propertyName);

		}

		//readonly static Dictionary<string, string> m_AttributeMap = new Dictionary<string, string>();

		static Character()
		{
			//m_AttributeMap.Add("ISP", "IspBase");
			//m_AttributeMap.Add("HP", "HpBase");
		}

		readonly static PropertyMetadataCollection s_AttributeList = MetadataCache.GetMetadata(typeof(Character)).Properties;

		static string MapAttribute(string attributeName)
		{
			if (s_AttributeList.Contains(attributeName) && s_AttributeList[attributeName].CanWrite)
				return attributeName;

			string baseProperty = attributeName + "Base";
			if (s_AttributeList.Contains(baseProperty) && s_AttributeList[baseProperty].CanWrite)
				return baseProperty;

			return attributeName; //the error will be handled elsewhere
		}


		internal int? GetAttribute(string attributeName)
		{
			var propertyName = MapAttribute(attributeName);
			var oldValue = (int?)Properties.Metadata.Properties[propertyName].InvokeGet(this);
			return oldValue;
		}

		//public OtherAttributeCollection OtherAttributes
		//{
		//	get { return GetNew<OtherAttributeCollection>("OtherAttributes"); }
		//}

	}

}
